/*
 * SimpleGrid for jQuery
 * http://code.google.com/p/jquerysimplegrid/
 *
 * Copyright (c) 2009 Jorge Condomi
 * Under the MIT licenses.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Date: 2009-10-17 08:00:00 -0300 (Sun, 17 Oct 2009)
 * Revision: < v1.0
 */

 /**
 *	Options for the grid itself:
 *		headers: null, 	[Object matrix containig the header info (see example)] *MANDATORY*
 *		remove: true,  	[It's the delete button available?]
 *		add: true,		[It's the add button available?]
 *		sort: true,		[Make it sortable?]
 *		page: 0,		[NOT IMPLEMENTED]
 *		edit: true,		[Editable?]
 *		source: '',		[NOT IMPLEMENTED]
 *		lang: 'en',		[The language of the grid, the file containing the translation has to be included prior the init]
 *		multiple: true,	[Is alow to hightlight more than 1 row? how many?]
 *		themePath: 'themes/light/' [Path relative to the desired theme folder, also used for some images]
 *
 ************
 *
 * Options for each header:
 *		editable: true, 		[Is editable the data from this column?]
 * 		type: 'text',			[The type of the data, external type has to be included prior the init] *MANDATORY*
 *		validate: false,		[NOT IMPLEMENTED]
 * 		validator: function(table,data){return true;}, [NOT IMPLEMENTED]
 * 		title:'',				[The title of the column] *MANDATORY*
 * 		name:'',				[PRIVATE]
 * 		data: '',				[NOT IMPLEMENTED]
 * 		key: false,				[Only the model key column has to have it in true] *MANDATORY JUST ONE COLUMN*
 * 		show: true,				[The column will appear visible in the table?]
 * 		defaultValue: '',		[The default value for the field, if the column is hidden this value will be sent on the Add action]
 * 		isNull: true,			[Validate the value of the column for existance]
 * 		actions: null,			[PRIVATE]
 * 		possibleValues:null		[Used for certain types, e.g. Object with "value":"DisplayValue", for selects]
 */

(function($){
	
    $.simpleGrid = function(el, data, target, options){
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;
        
        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el; 
		base.id = base.$el.attr("id");
		base.error = [];
		base.adding = [];
		base.editing = [];
		base.headers = null;
		base.table = null;
		base.selecting = 0;
		base.last_selected = null;
        base.edit_data = {};
		base.order_data = [];
		
        // Add a reverse reference to the DOM object
        base.$el.data("simpleGrid", base);
        
        base.init = function(){
            if(data == undefined || data == null) data = [];
            base.data = data;
			base.target = target;
			base.options = $.extend({},$.simpleGrid.defaultOptions, options);
			
			//get and extend the headers
			base.headers = base.getHeaders();
			
			//setup the basic state vars
			base.adding[base.id] = false;
			base.editing[base.id] = false;
			
			
			//drawing the markup
			base.$el.append('<form action="" class="simpleGrid_form" method="post"></form>');
			form = base.$el.find('form.simpleGrid_form');
			form.append('<table class="grid"></table>');
			base.table = form.find('table.grid');
			
			base.table.append('<thead><tr></tr></thead>');
			tr = base.table.find('thead tr');
			tr.append('<th class="selectAll"><input type="checkbox" name="selectAll" value="1" /></th>');
			for(header in base.headers){
				if(!base.headers[header].show) continue;
				base.formatTh(tr,base.headers[header]);
			}
			base.table.append('<tbody></tbody>');
			base.populateData(base.table.find('tbody'), base.data);
			base.table.append('\
			<tfoot>\
				<tr><td colspan="'+(base.table.find("thead th").length)+'">\
					<div id="grid_loading" style="display:none;">&nbsp;</div>\
					<div class="msg" style="display:none;">\
						<span class="message"></span>\
						<span class="btn msg_ok"><a href="#">'+base._('ok')+'</a></span>\
						<span class="btn msg_cancel"><a href="#">'+base._('cancel')+'</a></span>\
					</div>\
					<div class="control" style="display:none;">\
						<span class="btn ok"><a href="#">'+base._('save')+'</a></span>\
						<span class="btn cancel"><a href="#">'+base._('cancel')+'</a></span>\
					</div>\
					<div class="btns">\
						<span class="btn basic_btn add_row"><a href="#">+</a></span>\
						<span class="btn basic_btn del_rows"><a href="#">-</a></span>\
						<span class="btn basic_btn edit_rows"><a href="#">'+base._('edit')+'</a></span>\
					</div>\
					<div class="notice" style="display:none;">\
						<span class="message"></span>\
					</div>\
				</td></tr>\
			</tfoot>');
			
			base.makeSelectablesRows();

			base.table.find('a[href=#]').click(function(){return false;});
			 
			if(base.options.sort){
				base.table.find("thead th").not(".selectAll").toggle(
					function(){ base.gridOrder(base.headers[$(this).attr('rel')],'asc');},
					function(){ base.gridOrder(base.headers[$(this).attr('rel')],'desc');}
				);
			}
			
			//Is add enabled?
			if(base.options.add){
				base.table.find("tfoot tr td span.add_row a").click(function(){base.add()});
			}else{
				base.table.find("tfoot tr td span.add_row a").remove();
			}
			//Is remove enabled?
			if(base.options.remove){
				base.table.find("tfoot tr td span.del_rows a").click(function(){base.del()});
			}else{
				base.table.find("tfoot tr td span.del_rows a").remove();
			}
			
			//Is edit enabled?
			if(base.options.edit){
				base.table.find("tfoot tr td span.edit_rows a").click(function(){base.edit()});
			}else{
				base.table.find("tfoot tr td span.edit_rows a").remove();
			}
			
			base.table.find("thead th.selectAll input:checkbox").click(function(){
				var rows = base.table.find("tbody tr");
				if($(this).is(":checked")){
					console.log(rows);
					rows.each(function(){
						$(this).addClass('selected');
						$(this).find("input.grid_id:checkbox").attr("checked", "checked");
					});
					base.selecting = rows.length;
				}else{
					rows.each(function(){
						$(this).removeClass('selected');
						$(this).find("input.grid_id:checkbox").attr("checked", false);
					});
					base.selecting = 0;
				}
			});
			
			base.table.find("tbody tr td").each(function(){
				if($.browser.mozilla){
					$(this).css('MozUserSelect','none');
				}else if($.browser.msie){
					$(this).bind('selectstart',function(){return false;});
				}else{
					$(this).mousedown(function(){return false;});
				}
			});
			
        }
        
		/**
		*	Prepares the headers for the table
		*/
		base.getHeaders = function(){
			if(base.options.headers == null){
				base.headers = base.data[0];
				delete base.data[0];
			}else{
				base.headers = base.options.headers;
			}
			for(header in base.headers){
				base.headers[header] = $.extend({},$.simpleGrid.defaultColumnOptions, base.headers[header]);
				if($.simpleGrid.types[base.headers[header].type] != undefined){
					base.headers[header].actions = $.simpleGrid.types[base.headers[header].type];
				}else{
					base.headers[header].actions = $.simpleGrid.types['text'];
				}
				//Complete the action definition for the used type.
				base.headers[header].actions = $.extend($.simpleGrid.types.typesDefaultActions,base.headers[header].actions);
				base.headers[header].name = header;
			}
			return base.headers
		}
		
		/**
		*	Gives the propper format to the cell in the table header
		*/
		base.formatTh = function(tr, header){
			//maybe some arrows for the sorting?
			tr.append('<th class="type_'+header.type+'" rel="'+header.name+'">'+header.title+'</th>');
		}
		
		/**
		*	Inserts the data on the rows
		*/
		base.populateData = function(target, data){
			var tr;
			for(row in data){
				target.append('<tr/>');
				tr = target.find('tr:last');
				tr.data('dataValue',data[row]);
				tr.attr('rel',row)
				tr.append('<td><input type="checkbox" rel="'+options.name+'" name="grid_id[]" value="'+data+'" class="grid_id" /></td>');
				for(cell in data[row]){
					if(!base.headers[cell].show) continue;
					var td = $('<td/>');
					td.attr('rel',cell);
					td.data('dataValue',data[row][cell]);
					base.formatTd(td,data[row][cell],base.headers[cell]);
					tr.append(td)
				}
			}
			//base.table.find("tbody input.grid_id").hide();
		
			base.doZebra();
		}
		
		/**
		*	Eliminates all the info and render again the table
		*/
		base.renderAgain = function(target, data){
			base.populateData(target.empty(), data);
		}
		
		
		/**
		*	Append the propper field to the td
		*/
		base.formatTd = function(td, data, options){
			if(options.key == true){
				td.addClass('type_'+options.type).text(data);
			}else{
				td.append(options.actions.show(base, data, options));
			}
		}
		
		/**
		*	Append the edit cell to the tr
		*/
		base.formatEditTd = function(td, value, options){
			if(options.key == true){
				td.addClass('grid_non_editable').text('-');
			}else{
				td.append(options.actions.showEdit(base, value, options));
			}
		}
		
		
		/**
		*	Fetch the string from the selected language (my approach to i18n)
		*/
		base._ = function(string){
			return $.simpleGrid.text[base.options.lang][string];
		}
		
		/**
		*	Add selection events for rows
		*/
		base.makeSelectablesRows = function(){
			base.table.find("tbody tr").bind('click.selectable', function(event){
				if(base.adding[base.id]) return false;
				if(base.editing[base.id]) return false;
				if(!$(this).is('.selected')){
					if(!base.options.multiple && base.selecting > 0) return false;
					if(base.options.multiple && event.shiftKey && base.last_selected != null){
						var rows = base.table.find("tbody tr");
						var firstIndex = rows.index($(base.last_selected));
						var lastIndex = rows.index($(this));
						var end = Math.max(firstIndex, lastIndex);
						var start = Math.min(firstIndex, lastIndex);
						for(i=start; i <= end; i++){
							++base.selecting;
							$(rows[i]).addClass('selected');
							$(rows[i]).find('input.grid_id:checkbox').attr('checked','checked');
						}
					}else{
						++base.selecting;
						$(this).addClass('selected');
						$(this).find('input.grid_id:checkbox').attr('checked','checked');
					}
				}else{
					if(event.shiftKey && base.last_selected != null){
						var rows = base.table.find("tbody tr");
						var firstIndex = rows.index($(base.last_selected));
						var lastIndex = rows.index($(this));
						var end = Math.max(firstIndex, lastIndex);
						var start = Math.min(firstIndex, lastIndex);
						for(i=start; i <= end; i++){
							--base.selecting;
							$(rows[i]).removeClass('selected');
							$(rows[i]).find('input.grid_id:checkbox').attr('checked', false);
						}
					}
					--base.selecting;
					$(this).removeClass('selected');
					$(this).find('input.grid_id:checkbox').attr('checked', false);
				}
				base.last_selected = $(this);
			});
		}
		
		/**
		* Makes the rows Unselectables
		*/
		base.makeUnselectablesRows = function(){
			base.selecting = 0;
			base.table.find("tbody tr").unbind('click.selectable');
		}
		
		/**
		* Add zebra style to the grid
		*/
		base.doZebra = function(){
			base.table.find("tbody tr").removeClass("odd");
			base.table.find("tbody tr:odd").addClass("odd"); 
		}
		
		/**
		*	Order the data of the grid by a certain column
		*/
		base.gridOrder = function(header,order){
			if(base.adding[base.id]) return false;
			if(base.editing[base.id]) return false;
			var rows = base.table.find('tbody tr').get();
			rows.sort(function(a, b){
				var alpha = header.actions.getOrdenableValue(base, $(a).find('[rel='+header.name+']'), header)
				var beta = header.actions.getOrdenableValue(base, $(b).find('[rel='+header.name+']'), header)
				if(!isNaN(alpha) && !isNaN(beta)){
					alpha = parseFloat(alpha);
					beta = parseFloat(beta);
				}else{
					alpha = alpha.toLowerCase();
					beta = beta.toLowerCase();
				}
				if(typeof order != 'undefined' && order == 'asc'){
					return ((alpha < beta) ? 1 : ((alpha > beta) ? -1 : 0));
				}else{
					return ((alpha < beta) ? -1 : ((alpha > beta) ? 1 : 0));
				}
			});
			$.each(rows, function(index, row) {
				base.table.children('tbody').append(row);
			});
			base.doZebra();
		}
		
		/**
		*	Assign the properties for the ok and cancel buttons
		*/
		base.footerControlShow = function(ok, cancel) {
			var cont = base.table.find('tfoot tr:last td');
			var btns = base.table.find('div.control');
			btns.find('span.ok a').text(ok.value).unbind('click').click(ok.click);
			btns.find('span.cancel a').text(cancel.value).unbind('click').click(cancel.click);
			cont.find('div:visible').fadeOut('fast', function(){
				btns.fadeIn('fast');
			});
		}
		
		/**
		*	Shows the tfoot buttons
		*/
		base.footerBtnsShow = function() {
			var cont = base.table.find('tfoot tr:last td');
			cont.find('div:visible').fadeOut('fast', function(){
				cont.find('div.btns').fadeIn('fast');
			});
		}

		/**
		*	Sets up and create a modal message to wain user action
		*/
		base.footerModal = function(message, ok, cancel){
			var cont = base.table.find('tfoot tr:last td');
			cont.find('div:visible').fadeOut('fast', function(){
				cont.find('div.msg span.message').text(message);
				cont.find('div.msg').fadeIn('fast');
			});
			cont.find('span.msg_ok a').text(ok.value).unbind('click').click(ok.click);
			cont.find('span.msg_cancel a').text(cancel.value).unbind('click').click(cancel.click);
		}
		
		/**
		*	Sets up and shows a simple notice
		*/
		base.footerNotice = function(message){
			var cont = base.table.find('tfoot tr:last td');
			var actual = cont.find('div:visible').not('#grid_loading');
			cont.find('div:visible').fadeOut('fast', function(){
				cont.find('div.notice span.message').text(message);
				cont.find('div.notice').fadeIn('fast');
				setTimeout(function(){
						cont.find('div.notice').fadeOut('slow',function(){
							actual.fadeIn('slow');
							})
						},2000);
			});
		}
		
		/**
		*	Adds a new row to the main data and renders again the whole table
		*/
		base.addRowAndRender = function(newData){
			for(data in newData){
				base.data.push(newData[data]);
			}
			base.renderAgain(base.table.find('tbody'), base.data);
		};
		
		/**
		*	Delete the data for the indexes and renders again the whole table
		*/
		base.DeleteDataAndRender = function(indexes){
			for(index in indexes){
				delete base.data[indexes[index]];
				base.table.find('tbody tr[rel='+indexes[index]+']').remove();
			}
			base.renderAgain(base.table.find('tbody'), base.data);
		};
		
		/**
		*	Edit the data for the indexes and renders again the whole table
		*/
		base.EditDataAndRender = function(newData){
			for(data in newData){
				base.data[data] = newData[data];
				base.table.find('tbody tr[rel='+data+']').remove();
			}
			base.renderAgain(base.table.find('tbody'), base.data);
		};
		
		/**
		*	Creates a new row with the fields to add new data
		*/
		base.add = function(){
			if(base.adding[base.id]) return false;
			base.adding[base.id] = true;
			base.makeUnselectablesRows();
			base.table.find('tbody').append('<tr class="grid_add"></tr>');
			tr = base.table.find('tbody tr:last');
			for(name in base.headers){
				if(!base.headers[name].show) continue;
				var td = $('<td/>');
				td.attr('rel',name);
				base.formatEditTd(td, base.headers[name].defaultValue ,base.headers[name]);
				tr.append(td);
			}
			tr.find('input:first').focus();
			base.footerControlShow({
				'value' : base._('save'),
				'click' : function(){
					tr = base.table.find('tr.grid_add');
					data = {};
					for(name in base.headers){
						if(!base.headers[name].show){
							if(base.headers[name].key) continue;
							data[name] = base.headers[name].defaultValue;
							continue;
						}
						td = $(tr).find('td[rel='+name+']');
						value =  base.headers[name].actions.getValue(base, td);
						if(base.headers[name].isNull == false && (value == null || value == '')){
							base.footerNotice(base._('error_validating_null'));
							return false;
						}
						data[name] = value;
					}
					// all the data is gather
					base.table.find('tfoot div.msg,tfoot div.control').hide();
					base.showLoading();
					message = {"action": "add","data": [data]};
					base.doRequest(message, function(response){ //success
						if(response.status == 1){
							newData = {};
							if(response.data[0] != null){
								data = $.extend(response.data[0],data);
								base.addRowAndRender([data]);
							}else{
								base.footerNotice(base._('error_on_response'));
							}
						}else{
							base.footerNotice(base._('error_on_response'));
						}
					},function(XMLHttpRequest, textStatus, errorThrown){
						if(textStatus == 'parsererror'){
							
							base.footerNotice(base._('error_on_response'));
						}else{	
							base.footerNotice(base._('error_on_request'));
						}
						tr.remove();
					},function(XMLHttpRequest, textStatus){
						base.footerBtnsShow();
						base.makeSelectablesRows();
						base.hideLoading();
						base.adding[base.id] = false;
					});
					}
			},{
				'value' : base._('cancel'),
				'click' : function(){
					$(tr).remove();
					base.adding[base.id] = false;
					base.makeSelectablesRows();
					base.footerBtnsShow();
					}
			});
			base.doZebra();
		}
			
		/**
		*	Delete one o more rows
		*/
        base.del = function(){
			var checked = base.table.find('input.grid_id:checked');
			if(checked.length > 0){
				base.makeUnselectablesRows();
				base.footerModal(base._('sure_to_delete'),
				{
					'value' : base._('delete'),
					'click' : function(){
						base.table.find('tfoot div.msg,tfoot div.control').hide();
						base.showLoading();
						var ids = [];
						var key = '';
						var index = {};
						base.table.find('input.grid_id:checked').each(function(){
							ids.push({"id":$(this).val()});
							key = $(this).attr('rel');
							index[$(this).val()] = parseInt($(this).parent().parent().attr('rel'));
						});
						message = {"action": "delete","data": ids};
						base.doRequest(message, function(response){ //success
							if(response.status == 1){
								ids_erased = [];
								for(data in response.data){
									if(response.data[data].status == 1){ // good
										ids_erased.push(index[response.data[data][key]]);
									}
								}
								base.DeleteDataAndRender(ids_erased);
							}else{
								base.footerNotice(base._('error_on_request'));
							}
						},function(XMLHttpRequest, textStatus, errorThrown){ //error
							base.footerNotice(base._('error_on_request'));
						},function(XMLHttpRequest, textStatus){
							base.hideLoading();
							base.footerBtnsShow();
							base.makeSelectablesRows();
						});
					}
				},{
					'value' : base._('cancel'),
					'click' : function(){
						base.footerBtnsShow();
						base.makeSelectablesRows();
						}
				});
			}else{
				base.footerNotice(base._('need_to_select'));
			}
		}
		
		/**
		*	Live editing for the selected rows
		*/
		base.edit = function(){
			var fields = [];
			if(base.editing[base.id]) return false;
			base.editing[base.id] = true;
			var checked = base.table.find('tr.selected');
			if($(checked).length > 0){
				base.makeUnselectablesRows();
				$(checked).each(function(){
					var id = $(this).find('input.grid_id:checked').val();
					var row = $(this);
					var row_data = row.data('dataValue');
					base.edit_data[id] = {};
					for(name in base.headers){
						if(!base.headers[name].show) continue;
						if(!base.headers[name].editable) continue;
						var td = row.find('td[rel='+name+']');
						var data = row_data[name];
						row.addClass('grid_editing');
						base.edit_data[id][name] = data;
						td.empty();
						base.formatEditTd(td,data, base.headers[name]);
					}
				});
				base.footerControlShow({
					'value' : base._('save'),
					'click' : function(){
						var all_data = [];
						var key = '';
						var index = {};
						var ok = true;
						base.table.find('tr.grid_editing').each(function(){
							var data = {};
							var row_data = $(this).data('dataValue');
							index[$(this).find('input.grid_id:checked').val()] = $(this).attr('rel');
							for(name in base.headers){
								if(!base.headers[name].show || base.headers[name].key || !base.headers[name].editable){
									data[name] = row_data[name];
									if(base.headers[name].key){
										key = name;
									}
									continue;
								}
								td = $(this).find('td[rel='+name+']');
								value =  base.headers[name].actions.getValue(base, td);
								if(base.headers[name].isNull == false && (value == null || value == '')){
									base.footerNotice(base._('error_validating_null'));
									ok = false;
								}
								data[name] = value;
							}
							all_data.push(data);
						});
						if(!ok) return false;
						// all the data is gathered
						base.table.find('tfoot div.msg,tfoot div.control').hide();
						base.showLoading();
						message = {"action": "update","data": [all_data]};
						base.doRequest(message, function(response){ //success
							if(response.status == 1){
								newData = [];
								for(row in response.data){
									newData[index[response.data[row][key]]] = response.data[row];
								}
								base.EditDataAndRender(newData);
							}else{
								base.footerNotice(base._('error_on_response'));
							}
						},function(XMLHttpRequest, textStatus, errorThrown){
							if(textStatus == 'parsererror'){
								base.footerNotice(base._('error_on_response'));
							}else{	
								base.footerNotice(base._('error_on_request'));
							}
							tr.remove();
						},function(XMLHttpRequest, textStatus){
							base.footerBtnsShow();
							base.makeSelectablesRows();
							base.hideLoading();
							base.editing[base.id] = false;
						});
						}
					
				},{
					'value' : base._('cancel'),
					'click' : function(){
						base.table.find('tr.grid_editing').each(function(){
							var id = $(this).find('input.grid_id:checked').val();
							for(name in base.headers){
								if(!base.headers[name].show) continue;
								if(!base.headers[name].editable) continue;
								var td = $(this).find('td[rel='+name+']');
								var data = base.edit_data[id][name];
								$(this).removeClass('grid_editing');
								td.empty();
								base.formatTd(td,data,base.headers[name]);
							}
						});
						base.edit_data = {};
						base.editing[base.id] = false;
						base.makeSelectablesRows();
						base.footerBtnsShow();
						}
				});
			}else{
				base.footerNotice(base._('need_to_select'));
			}
		}
		
		/**
		*	Send the request to the target page, and return feedback
		*/
		base.doRequest = function(message, callback, error, complete){
			var result = false;
			message = base.jsonStringify(message);
			//$.post(base.target,message,complete,"json");
			//return true;
			$.ajax({
				type: "post",
				tcontentType: "application/json; charset=utf-8",
				url: base.target,
				data: {data: message},
				dataType: "json",
				success: callback,
				error: error,
				complete: complete
				
			});
			return result;
		}
		
		/**
		*	Show and Hide the loading dialog
		*/
		base.showLoading = function(){
			base.table.find("tfoot div:visible").not("#grid_loading").addClass('hidden_for_loading').fadeOut('fast');
			base.table.find("#grid_loading").fadeIn('fast');
		}
		
		/**
		*	Show and Hide the loading dialog
		*/
		base.hideLoading = function(){
			base.table.find("#grid_loading").fadeOut('fast',function(){
				base.table.find("tfoot div.hidden_for_loading").removeClass('hidden_for_loading').fadeIn('fast');
			});
		}
		
		/**
		*	Json stringify, only for old browser
		*/
		base.jsonStringify = function(data){
			var JSON = window.JSON;
		    if (typeof JSON === 'object' && typeof JSON.stringify === 'function') {
		        return JSON.stringify(data); // let the browser handle the job.
		    }
			var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		        gap, indent, meta = {'\b': '\\b', '\t': '\\t','\n': '\\n','\f': '\\f','\r': '\\r','"' : '\\"','\\': '\\\\'}, rep;
			function quote(string) {
				escapable.lastIndex = 0;
		        return escapable.test(string) ?
		            '"' + string.replace(escapable, function (a) {
		                var c = meta[a];
		                return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
		            }) + '"' : '"' + string + '"';
		    }
			function str(key, holder) {
		        var i, k, v,
		            length,
		            mind = gap,
		            partial,
		            value = holder[key];
				if (value && typeof value === 'object' &&
		                typeof value.toJSON === 'function') {
		            value = value.toJSON(key);
		        }
				if (typeof rep === 'function') {
		            value = rep.call(holder, key, value);
		        }
				switch (typeof value) {
		        case 'string': return quote(value);
				case 'number': return isFinite(value) ? String(value) : 'null';
				case 'boolean':
		        case 'null': return String(value);
				case 'object':
					if (!value) { return 'null';}
					gap += indent;
					partial = [];
					if (Object.prototype.toString.apply(value) === '[object Array]') {
						length = value.length;
						for (i = 0; i < length; i += 1) {
						}
						v = partial.length === 0 ? '[]' :
							gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
								'[' + partial.join(',') + ']';
						gap = mind;
						return v;
					}
					if (rep && typeof rep === 'object') {
						length = rep.length;
						for (i = 0; i < length; i += 1) {
							k = rep[i];
							if (typeof k === 'string') {
								v = str(k, value);
								if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); }
							}
						}
					} else {
						for (k in value) {
							if (Object.hasOwnProperty.call(value, k)) {
								v = str(k, value);
								if (v) {  partial.push(quote(k) + (gap ? ': ' : ':') + v); }
							}
						}
					}
					v = partial.length === 0 ? '{}' :
						gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
					gap = mind;
		            return v;
		        }
		    }
			return str('', {'': data});
		}
		
		/**
		*	Init all the magic
		*/
        base.init();
    }

	
	$.simpleGrid.typesDefaultActions = {
		getValue: function(grid, parent){
		},
		setValue: function(grid, parent){
		},
		show:  function(grid, value, options){
		},
		showEdit: function(grid, value, options){
		},
		getOrdenableValue: function(grid, parent){
		},
		doBeforeSubmitAdd: function(grid){
		},
		doAfterSubmitAdd: function(grid){
		},
		doBeforeSubmitEdit: function(grid){
		},
		doAfterSubmitEdit: function(grid){
		},
		doBeforeSubmitDelete: function(grid){
		},
		doAfterSubmitDelete: function(grid){
		}
	}
	
	$.simpleGrid.types = {
		'text' : {
				getValue: function(grid, parent){
					return $(parent).find('input').val();
				},
				show:  function(grid, value, options){
					return value;
				},
				showEdit: function(grid, value, options){
					if(!options.isNull){
						return 	$('<input type="text" class="grid_text not_null" name="'+options.name+'" value="'+value+'" />');
					}else{
						return 	$('<input type="text" class="grid_text" name="'+options.name+'" value="'+value+'" />');
					}
				},
				getOrdenableValue: function(grid, parent, options){
					return $(parent).text();
				}
			},
		'boolean' : {
				getValue: function(grid, parent){
					return ($(parent).find('input:checkbox').is(':checked'));
				},
				show:  function(grid, value, options){
					if(value == true){
						return $('<input type="checkbox" class="grid_boolean" disabled="disabled" checked="checked" name="'+options.name+'" value="true" />');
					}else{
						return $('<input type="checkbox" class="grid_boolean" readonly="readonly" name="'+options.name+'" value="" />');
					}
				},
				showEdit: function(grid, value, options){
					if(value == true || (options.defaultValue == true && value == null)){
						return $('<input type="checkbox" checked="checked" class="grid_boolean" name="'+options.name+'" value="true" />');
					}else{
						return $('<input type="checkbox" class="grid_boolean" name="'+options.name+'" value="true" />');
					}
				},
				getOrdenableValue: function(grid, parent, options){
					if(($(parent).find('.grid_boolean').is(':checked'))){
						return 1;
					}
					return 0;
				}
		},
		'select' : {
				getValue: function(grid, parent){
					return $(parent).find('select').val();
				},
				show:  function(grid, value, options){
					return options.possibleValues[value];
				},
				showEdit: function(grid, value, options){
					var select = $('<select class="grid_select" name="'+options.name+'" />');
					for(name in options.possibleValues){
						if(value == name && value != null){
							
							var option = $('<option value="'+name+'" selected="selected"/>').text(options.possibleValues[name]);
							
						}else{
							var option = $('<option value="'+name+'"/>').text(options.possibleValues[name]);
						}
						select.append(option);
					}
					return select;
				},
				getOrdenableValue: function(grid, parent, options){
					return $(parent).text();
				}
		}
	}


	$.simpleGrid.defaultColumnOptions = {
		editable: true,
		type: 'text',
		validate: false,
		validator: function(table,data){return true;},
		title:'',
		name:'',
		data: '',
		key: false,
		show: true,
		defaultValue: '',
		isNull: true,
		actions: null,
		possibleValues:null
	}

	
    $.simpleGrid.defaultOptions = {
		headers: null,
        remove: true,
        add: true,
        sort: true,
        page: 0,
        edit: true,
		source: '',
		lang: 'en',
		multiple: true,
		themePath: 'themes/light/'
    }
	
	$.simpleGrid.text = {}
	$.simpleGrid.text.en = {
		'save' : 'Save',
		'cancel' : 'Cancel',
		'ok'	: 'OK',
		'delete' : 'Delete',
		'edit' : 'Edit',
		'add' : 'Add',
		'sure_to_delete' : 'Are you sure to delete the selected records?',
		'sure_to_cancel' : 'All the data would be reseted',
		'need_to_select' : 'You must select at least one row',
		'error_on_request' : 'There was an error requesting to the server',
		'error_on_response' : 'There was and error parsing the server\'s response',
		'error_validating_null' : 'All the required data has to be completed'
	}

    $.fn.simpleGrid = function(data, target, options){
        return this.each(function(){
            (new $.simpleGrid(this, data, target, options));
        });
    }

	
})(jQuery);